package cn.iocoder.yudao;

import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaAnnotation;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import static java.lang.Character.isUpperCase;

public class JavaToSqlTableConverter {
    /**
     * 生成创建表的 SQL 语句
     *
     * @param clazz    实体类的 Class 对象
     * @param javaPath 实体类的 Java 文件路径
     * @return 创建表的 SQL 语句
     */
    public static void generateCreateTableSQL(Class<?> clazz, String javaPath) {
        TableName tableNameAnnotation = clazz.getAnnotation(TableName.class);
        if (tableNameAnnotation == null) {
            throw new IllegalArgumentException("Class must have TableName annotation");
        }

        Map<String, String> commentMap = putCommentByJavaFile(javaPath);

        String tableName = tableNameAnnotation.value();
        StringBuilder sql = new StringBuilder();
        sql.append("drop table if exists `").append(tableName).append("`;\n");
        sql.append("CREATE TABLE `").append(tableName).append("` (\n");

        Map<String, String> typeToColumn = getTypeToColumnMap();

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String columnName = field.getName();
            columnName = convertCamelCaseToUnderscore(columnName);

            String columnType = typeToColumn.get(field.getType().getSimpleName());
            String columnComment = commentMap.get(field.getName());
            if (StringUtils.isNotBlank(columnComment)) {
                columnComment = columnComment.replaceAll("\\n", " ");
            }

            if (columnType != null) {
                sql.append("  `").append(columnName).append("` ").append(columnType);
                if (field.isAnnotationPresent(TableId.class)) {
                    sql.append(" NOT NULL AUTO_INCREMENT");
                } else {
                    sql.append(" NOT NULL");
                }
                sql.append(" COMMENT '").append(columnComment).append("',\n");
            }
        }

        sql.append(getCommonFieldsSQL());
        sql.append(getPrimaryKeyConstraintSQL());
        sql.append(getTableCommentSQL(commentMap.get("tableComment")));
        System.out.println(sql.toString());
    }

    /**
     * 将驼峰命名转换为下划线命名
     *
     * @param columnName 驼峰命名的列名
     * @return 下划线命名的列名
     */
    private static String convertCamelCaseToUnderscore(String columnName) {
        StringBuilder convertedName = new StringBuilder();
        for (int i = 0; i < columnName.length(); i++) {
            if (isUpperCase(columnName.charAt(i))) {
                convertedName.append("_");
            }
            convertedName.append(columnName.charAt(i));
        }
        return convertedName.toString().toLowerCase(Locale.ROOT);
    }

    /**
     * 获取公共字段的 SQL 语句
     *
     * @return 公共字段的 SQL 语句
     */
    private static String getCommonFieldsSQL() {
        StringBuilder sql = new StringBuilder();
        sql.append("  `creator` varchar(64) NULL     DEFAULT '' COMMENT '创建者',\n");
        sql.append("  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n");
        sql.append("  `updater` varchar(64) NULL     DEFAULT '' COMMENT '更新者',\n");
        sql.append("  `update_time`     datetime    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n");
        sql.append("  `deleted` bit(1) NOT NULL DEFAULT false COMMENT '是否删除',\n");
        sql.append("  `tenant_id` bigint      not null default '0' COMMENT '租户编号',\n");
        return sql.toString();
    }

    /**
     * 获取主键约束的 SQL 语句
     *
     * @return 主键约束的 SQL 语句
     */
    private static String getPrimaryKeyConstraintSQL() {
        return "  PRIMARY KEY (`id`)\n";
    }

    /**
     * 获取表注释的 SQL 语句
     *
     * @param tableComment 表注释
     * @return 表注释的 SQL 语句
     */
    private static String getTableCommentSQL(String tableComment) {
        return ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='" + tableComment + "';\n";
    }

    /**
     * 根据 Java 文件路径获取注释信息
     *
     * @param path Java 文件路径
     * @return 注释信息的 Map，key 为字段名或表注释，value 为注释内容
     */
    public static Map<String, String> putCommentByJavaFile(String path) {
        JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();
        try {
            javaProjectBuilder.addSource(new File(path));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        Map<String, String> commentMap = new HashMap<>();
        Collection<JavaClass> classes = javaProjectBuilder.getClasses();

        for (JavaClass javaClass : classes) {
            commentMap.put("tableComment", javaClass.getComment());
            List<JavaField> fields = javaClass.getFields();
            fields.forEach(field -> commentMap.put(field.getName(), field.getComment()));
        }

        return commentMap;
    }

    /**
     * 根据文件夹路径生成 SQL 语句
     *
     * @param folder 文件夹路径
     */
    public static void generateSqlByFolder(String folder) {
        JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();
        javaProjectBuilder.addSourceTree(new File(folder));

        Collection<JavaClass> classes = javaProjectBuilder.getClasses();
        for (JavaClass javaClass : classes) {
            Map<String, String> commentMap = new HashMap<>();
            commentMap.put("tableComment", javaClass.getComment());

            JavaAnnotation tableNameAnnotation = getTableNameAnnotation(javaClass);
            String tableName = getTableName(tableNameAnnotation);
            StringBuilder sql = new StringBuilder();
            sql.append("drop table if exists `").append(tableName).append("`;\n");
            sql.append("CREATE TABLE `").append(tableName).append("` (\n");

            Map<String, String> typeToColumn = getTypeToColumnMap();

            List<JavaField> fields = javaClass.getFields();
            fields.forEach(field -> {
                String columnComment = getColumnComment(field);
                String columnName = getColumnName(field);
                String columnType = getColumnType(field, typeToColumn);

                if (columnType != null) {
                    sql.append("  `").append(columnName).append("` ").append(columnType);
                    if (isTableIdField(field)) {
                        sql.append(" NOT NULL AUTO_INCREMENT");
                    } else {
                        sql.append(" NOT NULL");
                    }
                    sql.append(" COMMENT '").append(columnComment).append("',\n");
                }
            });

            sql.append(getCommonFieldsSQL());
            sql.append(getPrimaryKeyConstraintSQL());
            sql.append(getTableCommentSQL(commentMap.get("tableComment")));

            System.out.println(sql.toString());
        }
    }

    /**
     * 获取字段的注释
     *
     * @param field JavaField 对象
     * @return 字段的注释
     */
    private static String getColumnComment(JavaField field) {
        String columnComment = field.getComment();
        if (StringUtils.isNotBlank(columnComment)) {
            columnComment = columnComment.replaceAll("\\n", " ");
        } else {
            columnComment = "";
        }
        return columnComment;
    }

    /**
     * 获取字段的列名
     *
     * @param field JavaField 对象
     * @return 字段的列名
     */
    private static String getColumnName(JavaField field) {
        String columnName = field.getName();
        StringBuilder convertedName = new StringBuilder();
        for (int i = 0; i < columnName.length(); i++) {
            if (isUpperCase(columnName.charAt(i))) {
                convertedName.append("_");
            }
            convertedName.append(columnName.charAt(i));
        }
        return convertedName.toString().toLowerCase(Locale.ROOT);
    }

    /**
     * 获取字段的列类型
     *
     * @param field        JavaField 对象
     * @param typeToColumn 类型到列类型的映射
     * @return 字段的列类型
     */
    private static String getColumnType(JavaField field, Map<String, String> typeToColumn) {
        return typeToColumn.get(field.getType().getSimpleName());
    }

    /**
     * 判断字段是否为主键字段
     *
     * @param field JavaField 对象
     * @return 是否为主键字段
     */
    private static boolean isTableIdField(JavaField field) {
        return field.getAnnotations().stream().anyMatch(annotation -> annotation.getType().getName().equals(TableId.class.getSimpleName()));
    }

    /**
     * 获取类型到列类型的映射
     *
     * @return 类型到列类型的映射
     */
    private static Map<String, String> getTypeToColumnMap() {
        Map<String, String> typeToColumn = new HashMap<>();
        typeToColumn.put(Long.class.getSimpleName(), "BIGINT(20)");
        typeToColumn.put(String.class.getSimpleName(), "VARCHAR(255)");
        typeToColumn.put(List.class.getSimpleName(), "VARCHAR(255)");
        typeToColumn.put(BigDecimal.class.getSimpleName(), "DECIMAL(19,2)");
        typeToColumn.put(Date.class.getSimpleName(), "DATETIME");
        typeToColumn.put(Double.class.getSimpleName(), "DOUBLE");
        typeToColumn.put(Integer.class.getSimpleName(), "INT(11)");
        typeToColumn.put("int", "INT(11)");
        typeToColumn.put("boolean", "BOOLEAN");
        typeToColumn.put("float", "DECIMAL(19,2)");
        typeToColumn.put("double", "DECIMAL(19,2)");
        typeToColumn.put(Boolean.class.getSimpleName(), "BOOLEAN");
        typeToColumn.put(Float.class.getSimpleName(), "DECIMAL(19,2)");
        typeToColumn.put(LocalDateTime.class.getSimpleName(), "DATETIME");
        return typeToColumn;
    }

    /**
     * 获取表名注解
     *
     * @param javaClass JavaClass 对象
     * @return 表名注解
     */
    private static JavaAnnotation getTableNameAnnotation(JavaClass javaClass) {
        return javaClass.getAnnotations().stream().filter(annotation -> annotation.getType().getName().equals(TableName.class.getSimpleName())).findFirst().orElse(null);
    }

    /**
     * 获取表名
     *
     * @param tableNameAnnotation 表名注解
     * @return 表名
     */
    private static String getTableName(JavaAnnotation tableNameAnnotation) {
        if (tableNameAnnotation == null) {
            throw new IllegalArgumentException("Class must have TableName annotation");
        }
        return tableNameAnnotation.getProperty("value").toString().replaceAll("\"", "").replaceAll("\\n", " ");
    }


    public static void main(String[] args) {
//        String folder = "/Users/luobotao/workspace/kezhan/ruoyi-vue-pro/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition";
//        JavaToSqlTableConverter.generateSqlByFolder(folder);
        String filePath = "/Users/luobotao/workspace/kezhan/ruoyi-vue-pro/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/cc/BpmProcessInstanceCopyDO.java";
        JavaToSqlTableConverter.generateCreateTableSQL(BpmProcessInstanceCopyDO.class, filePath);
    }
}
